// --------------------------------------------------------------------------------------------------------------------- 
// <copyright file="T4TemplateBuilder.cs" company="Microsoft.Blueprints.Contrib Team">
//   (C) 2009 Microsoft.Blueprints.Contrib Team
// </copyright>
// <summary>
//   Defines the T4TemplateBuilder type.
// </summary>
// ---------------------------------------------------------------------------------------------------------------------

namespace Microsoft.Blueprints.Contrib.T4
{
    #region Using Directives

    using System;
    using System.IO;

    using Microsoft.SoftwareFactories.Blueprints;

    #endregion

    /// <summary>
    /// Fluent Interface for building T4 Templates
    /// </summary>
    public class T4TemplateBuilder
    {
        /// <summary>
        /// Instance of the Blueprint Manager content
        /// </summary>
        private readonly BPMContextBase context;

        /// <summary>
        /// Defines the template to build
        /// </summary>
        private readonly T4Template template;

        /// <summary>
        /// Instance of the T4Resouce within the Blueprint Manager
        /// </summary>
        private IT4ResourceAPI resourceAPI;

        /// <summary>
        /// Initializes a new instance of the <see cref="T4TemplateBuilder"/> class.
        /// </summary>
        /// <param name="context">
        /// BPMContextBase object passed in by Blueprints Manager
        /// </param>
        private T4TemplateBuilder(BPMContextBase context)
        {
            this.context = context;
            this.template = new T4Template();
        }

        /// <summary>
        /// Initialises the T4TemplateBuilder
        /// </summary>
        /// <param name="context">
        /// BPMContextBase object passed in by Blueprints Manager
        /// </param>
        /// <returns>
        /// A new instance of the T4TemplateBuilder
        /// </returns>
        public static T4TemplateBuilder Initialise(BPMContextBase context)
        {
            return new T4TemplateBuilder(context);
        }

        /// <summary>
        /// Dynamically populates the properties collection based on the 
        /// property directives present in the template.
        /// </summary>
        /// <returns>
        /// The T4TemplateBuilder object.
        /// </returns>
        public T4TemplateBuilder DynamicallyGenerateProperties()
        {
            if (this.template.Content.Length == 0)
            {
                throw new InvalidOperationException("Template must be populated before this method is called. Ensure you have called .LoadTemplate()");
            }

            this.template.PopulateProperties();

            return this;
        }

        /// <summary>
        /// Use to set the dynamicUpdate of Dynamically Generated properties.
        /// </summary>
        /// <param name="dynamicUpdate">
        /// The dynamicUpdate of the dynamically obtained properties
        /// </param>
        /// <returns>
        /// The T4TemplateBuilder object.
        /// </returns>
        public T4TemplateBuilder DynamicallySetPropeties(DynamicUpdate dynamicUpdate)
        {
            this.template.Properties = dynamicUpdate.Invoke(this.template).Properties;

            return this;
        }

        /// <summary>
        /// Call to end the Build process and return the constructed template.
        /// </summary>
        /// <returns>
        /// The constructed template.
        /// </returns>
        public T4Template Finish()
        {
            return this.template;
        }

        /// <summary>
        /// Loads the template into the object.
        /// </summary>
        /// <returns>
        /// The T4TemplateBuilder object
        /// </returns>
        public T4TemplateBuilder LoadTemplate()
        {
            if (this.resourceAPI == null)
            {
                this.InitialiseResourceAPI();
            }

            if (this.resourceAPI != null)
            {
                this.template.ContentFilePath = this.context.ContextServices.ContentFilePathname(this.template.FileName);
                this.template.Content = File.ReadAllText(this.template.ContentFilePath);
            }

            return this;
        }

        /// <summary>
        /// Saves the rendered content of the template to the path / file specified
        /// </summary>
        /// <returns>
        /// The T4TemplateBuilder object
        /// </returns>
        public T4TemplateBuilder SaveToFile()
        {
            string targetDirectory = Path.GetDirectoryName(this.template.TargetFileName);

            if (!Directory.Exists(targetDirectory))
            {
                Directory.CreateDirectory(targetDirectory);
            }

            File.WriteAllText(this.template.TargetFileName, this.template.RenderedContent);

            return this;
        }

        /// <summary>
        /// Sets a property on the teaplate
        /// </summary>
        /// <param name="key">
        /// The name of the property to set
        /// </param>
        /// <param name="value">
        /// The value to add to the property
        /// </param>
        /// <returns>
        /// The T4TemplateBuilder object
        /// </returns>
        public T4TemplateBuilder SetProperty(string key, string value)
        {
            this.template.Properties.Add(key, new T4PropertyData(value, typeof(string)));

            return this;
        }

        /// <summary>
        /// Sets the FileName of the Template
        /// </summary>
        /// <param name="path">
        /// The path of the target file
        /// </param>
        /// <param name="fileName">
        /// The file name.
        /// </param>
        /// <returns>
        /// The T4TemplateBuilder object
        /// </returns>
        public T4TemplateBuilder SetTargetFileName(string path, string fileName)
        {
            this.template.TargetFileName = Path.Combine(path, fileName);

            return this;
        }

        /// <summary>
        /// Sets the FileName of the Template
        /// </summary>
        /// <param name="fileName">
        /// The file name.
        /// </param>
        /// <returns>
        /// The T4TemplateBuilder object
        /// </returns>
        public T4TemplateBuilder SetTemplateFileName(string fileName)
        {
            this.template.FileName = fileName;

            return this;
        }

        /// <summary>
        /// Support method for calling T4 transformations
        /// </summary>
        /// <returns>
        /// string containing the rendered content
        /// </returns>
        public T4TemplateBuilder Transform()
        {
            if (this.resourceAPI == null)
            {
                this.InitialiseResourceAPI();
            }

            if (this.resourceAPI != null)
            {
                if (this.template.Content.Length == 0)
                {
                    this.LoadTemplate();
                }

                this.template.RenderedContent = this.resourceAPI.Render(
                        this.context, this.template.Content, this.template.ContentFilePath, this.template.Properties);
            }
            else
            {
                throw new Exception("Unable to load T4 Resource!");
            }

            return this;
        }

        /// <summary>
        /// Initialises the T4 ResourceAPI.
        /// </summary>
        private void InitialiseResourceAPI()
        {
            object resource = BPMServices.BPMGetResource(new Guid(BPMConstants.T4ResourceGuid));

            this.resourceAPI = resource as IT4ResourceAPI;
        }
    }
}